/*
 * Copyright (C) 2010 Google Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.appengine.tools.admin;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Max Ross <max.ross@gmail.com>
 */
public class OnesieClient {

  private final PrintWriter errorWriter;
  private final AppAdminFactory.ConnectOptions options;
  private final File classFile;
  private final String mainClass;

  public static void main(String[] args) throws IOException {
    String server = args[0];
    String classOutputPath = args[1];
    String mainClass = args[2];
    AppAdminFactory.ConnectOptions options = newConnectOptions(server);
    PrintWriter logWriter;
    try {
      File logFile = File.createTempFile("onesie", ".log");
      logWriter = new PrintWriter(new FileWriter(logFile), true);
    } catch (IOException e) {
      throw new RuntimeException("Unable to enable logging.", e);
    }
    OnesieClient client = new OnesieClient(options, logWriter, classOutputPath, mainClass);
    client.uploadAndRun();
  }

  private OnesieClient(AppAdminFactory.ConnectOptions options, PrintWriter errorWriter,
                       String classOutputPath, String mainClass) {
    this.options = options;
    this.errorWriter = errorWriter;
    this.classFile = new File(classOutputPath, mainClass.replace(".", "/") + ".class");
    this.mainClass = mainClass;
  }

  private static AppAdminFactory.ConnectOptions newConnectOptions(String server) {
    AppAdminFactory.ConnectOptions opts = new AppAdminFactory.ConnectOptions();
    opts.setServer(server);
    opts.setSecure(false);
    AppAdminFactory.PasswordPrompt pp = new AppAdminFactory.PasswordPrompt() {
      public String getPassword() {
        return new String(System.console().readPassword());
      }
    };
    opts.setPasswordPrompt(pp);
    return opts;
  }

  private void uploadAndRun() throws IOException {
    System.out.println("Executing " + mainClass + " on " + options.getServer() + "...");
    System.out.println();
    ServerConnection connection = ServerConnectionFactory.getServerConnection(options);
    String url = getUploadUrl(connection);
    uploadCode(connection, url);
  }

  private String getUploadUrl(ServerConnection connection) {
    Map<String, String> params = new HashMap<String, String>();
    params.put("uploadRedirectUrl", "/onesie?className=" + mainClass);
    try {
      return connection.get("/onesie", params).trim();
    } catch (Throwable t) {
      errorWriter.println("Unable to get upload url:");
      t.printStackTrace(errorWriter);
      throw new AdminException("Unable to get upload url: " + t.getMessage(), t);
    }
  }

  private void uploadCode(ServerConnection connection, String url) throws IOException {
    // Local blobstore url is just a relative path.
    // Prod blobstore is a full url.
    String postUrl;
    if (url.startsWith("http")) {
      postUrl = url;
    } else {
      postUrl = String.format("http://%s%s", options.getServer(), url);
    }
    URLConnection conn = new URL(postUrl).openConnection();
    ClientHttpRequest req = new ClientHttpRequest(conn);
    FileInputStream fis = new FileInputStream(classFile);
    req.setParameter("file", classFile.getName(), fis);
    InputStream is = req.post();
    BufferedReader in = new BufferedReader(new InputStreamReader(is));
    try {
      String inputLine;
      while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
      }
    } finally {
      in.close();
      System.out.println();
      System.out.println("Done!");
    }
  }
}
